import { HttpService, Injectable, UseInterceptors } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { User } from 'src/entities/User';
import { UserWallet } from 'src/entities/UserWallet';
import { RedisService } from 'src/redis/redis.service';
import { getTime } from 'src/util';
import { XjwtService } from 'src/xjwt/xjwt.service';
import { Connection } from 'typeorm';
import { UserService } from '../user/user.service';

@Injectable()
export class LoginService {
    constructor(
        private configService:ConfigService,
        private redis:RedisService,
        private httpService:HttpService,
        private userService:UserService,
        private xjwtService:XjwtService,
        private connection:Connection
        
    ) {}

    async login(code : string) {
        let lockKey = ''
        
        try {
            let wxConfig = this.configService.get('wechat')
            const { data } = await this.httpService.get(wxConfig.authCode2SessionUrl, {
                timeout : 10000,
                params : {
                    js_code : code,
                    appid : wxConfig.appid,
                    secret : wxConfig.secret
                }
            }).toPromise()

            if(data.errcode) {
                return null
            }

            const { openid, session_key, unionid } = data

            lockKey = 'login:' + openid

            const lock = await this.redis.set(lockKey, '', 'EX', 10, 'NX') //加个锁防止
            if(!lock) return null

            const time = getTime()

            let userId:number = 0

            //查用户
            let userRecord = await this.userService.getUserByOpenid(openid)
            if(!userRecord) { //新用户
                const queryRunner = this.connection.createQueryRunner();
                await queryRunner.connect();
                await queryRunner.startTransaction()

                try {
                    {
                        const { raw } = await queryRunner.manager.insert<User>(User, {openid, unionid : unionid ? unionid : '', sessionKey : session_key, registerTime : time, lastLoginTime : time})
                        if(!raw || raw.affectedRows < 1) {
                            queryRunner.rollbackTransaction()
                            return null
                        }
                        userId = raw.insertId
                    }

                    {
                        const { raw } = await queryRunner.manager.insert<UserWallet>(UserWallet, {userId : userId})
                        if(!raw || raw.affectedRows < 1) {
                            queryRunner.rollbackTransaction()
                            return null
                        }
                    }

                    await queryRunner.commitTransaction()

                    this.userService.setOpenIdToUserId(openid, userId)
                    this.userService.cacheUser(userId, {openid, sessionKey : session_key, unionid, userId})

                    return this.makeToken(userId, null, time)

                } catch (error) {
                    await queryRunner.rollbackTransaction()
                }finally {
                    await queryRunner.release()
                }

            }else { //再次登录
                userId =  userRecord.userId
                let update:any = {sessionKey : session_key}
                if(!userRecord.unionid) {
                    update.unionid = userRecord.unionid
                }

                this.userService.cacheUser(userId, update)
                let updateRows = await this.userService.update(userId, {lastLoginTime : time, ...update})
                if(!updateRows) return null

                return {
                    ...this.makeToken(userId, null, time),
                    avatar : userRecord.avatar || '',
                    nickname : userRecord.nickname || ''
                }
            }

        } catch (error) {
            console.log(error)
        }finally{
            if(lockKey) {
                this.redis.del(lockKey)
            }
        }

        return null
    }


    refresh(userId:number, token:string) {
        const jwtConfig = this.configService.get('jwt')

        let res = this.xjwtService.verify(token, {
            secret : jwtConfig.refreshLogin.secret
        })

        if(!res) return null

        const key = (res.key + jwtConfig.refreshLogin.scale) / jwtConfig.refreshLogin.scale
        if(key != userId) return null

        
        return this.makeToken(userId, jwtConfig)



    }

    makeToken(userId:number, jwtConfig?:any, time?:number) {
        if(!jwtConfig) {
            jwtConfig = this.configService.get('jwt')
        }

        if(!time) {
            time = getTime()
        }
        
        let accessToken = this.xjwtService.sign({key : userId * jwtConfig.login.scale + jwtConfig.login.scale, id : Math.ceil((time - 1030) / 263), time : time}, {
            secret : jwtConfig.login.secret,
            expiresIn : jwtConfig.expire
        })

        let refreshToken = this.xjwtService.sign({key : userId * jwtConfig.refreshLogin.scale - jwtConfig.refreshLogin.scale, rid : Math.ceil((time - 8030) / 463), time : time}, {
            secret : jwtConfig.refreshLogin.secret,
            expiresIn : jwtConfig.expire
        })



        return {
            accessToken,
            refreshToken,
            expire : jwtConfig.expire
        }

    }
    
}
